{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Setup\n",
    "\n",
    "This notebook contains useful functions used in other notebooks. In fact, it is run at the beginnig of the other notebooks."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Here we import libraries and utility functions\n",
    "import numpy as np\n",
    "import scipy\n",
    "from scipy import special\n",
    "import matplotlib.pyplot as plt\n",
    "import utilities as utils\n",
    "from keras.models import load_model\n",
    "from keras.backend import clear_session\n",
    "from keras.backend.tensorflow_backend import set_session\n",
    "import tensorflow as tf\n",
    "\n",
    "# This function estimates the ack probabilities by applying the trained neural network model\n",
    "def predict_ann_ack_probability( channel_coeff,NROF_MCS, NROF_SNRS,model ):\n",
    "    nrof_samples, _, _ = channel_coeff.shape\n",
    "\n",
    "    channel_coeff_concat = np.concatenate( ( np.real( channel_coeff ), np.imag( channel_coeff ) ), axis = 1 )\n",
    "    \n",
    "    ann_predicted_ack_prob = np.ndarray( [ nrof_samples, NROF_MCS, NROF_SNRS ] )\n",
    "    for snr_index in range( NROF_SNRS ):\n",
    "        ann_predicted_ack_prob[ :, :, snr_index ] = model.predict( [ channel_coeff_concat[ :, :, snr_index ] ] )\n",
    "        \n",
    "    return ann_predicted_ack_prob\n",
    "################################################################################\n",
    "\n",
    "# This function estimates the ack probabilities by applying the E2E trained neural network model\n",
    "def predict_ann_e2e_ack_probability( model, channel_coeff, nrof_mcs, mem = 1 ):\n",
    "    nrof_samples, _, nrof_snrs = channel_coeff.shape\n",
    "    \n",
    "    channel_coeff_concat = np.concatenate( ( np.real( channel_coeff ), np.imag( channel_coeff ) ), axis = 1 )\n",
    "    \n",
    "    ann_e2e_predicted_ack_prob = np.zeros( ( nrof_samples, nrof_mcs, nrof_snrs ) )\n",
    "    for snr_index in range( nrof_snrs ):\n",
    "        stacked_channel_coeff = utils.stack_features( channel_coeff_concat[ :, :, snr_index ], mem )\n",
    "        ann_e2e_predicted_ack_prob[ :, :, snr_index ] = model.predict( [ stacked_channel_coeff ] )\n",
    "        \n",
    "    return ann_e2e_predicted_ack_prob\n",
    "################################################################################\n",
    "\n",
    "# This function computes the throughput, error rate, ack probabilities and MSE \n",
    "# of the three approaches, i.e. delay blind, hybrid, and E2E, on the test set, i.e., FADING_CHANNEL_DATAFILE\n",
    "def computation( FADING_CHANNEL_DATAFILE,\n",
    "                 ANN_MODEL_FILE,\n",
    "                 ANN_MODEL_FILE_DELAYED,\n",
    "                 E2E_MCS_PRED_MODEL,\n",
    "                 DELAY,\n",
    "                 doppler_frequency,\n",
    "                 noise=True,\n",
    "                 train_fraction=0.2):  \n",
    "    \n",
    "    config = tf.ConfigProto()\n",
    "    config.gpu_options.allow_growth = True  \n",
    "\n",
    "    sess = tf.Session( config = config )\n",
    "    set_session(sess) \n",
    "\n",
    "    # Load the dataset and extract the test frames\n",
    "    DATASET = np.load( FADING_CHANNEL_DATAFILE, allow_pickle = True )[()]\n",
    "\n",
    "    start_test_frame = int( train_fraction * DATASET['channel'].shape[0] )\n",
    "    \n",
    "    DATASET['channel'] = DATASET['channel'][ start_test_frame :, ... ]\n",
    "    DATASET['block_success'] = DATASET['block_success'][ start_test_frame :, ... ]\n",
    "    \n",
    "    # Initialize simulatiom parameters\n",
    "    delta = DELAY\n",
    "    NROF_FRAMES, NROF_SUBCARRIERS, NROF_SNRS = DATASET['channel'].shape\n",
    "    NROF_MCS = len( DATASET[ 'block_sizes' ] )\n",
    "    \n",
    "    #Number of Wiener filter coefficients\n",
    "    N=10\n",
    "    \n",
    "    channel_sampling_interval=0.001 #1ms\n",
    "\n",
    "    \n",
    "    channel_coeff_noiseless = utils.calculate_channel_coefficients_scaled( DATASET[ 'channel' ],\n",
    "                                                                 DATASET['snrs_db'],\n",
    "                                                                 channel_estimation_noise = False)\n",
    "    channel_coeff = utils.calculate_channel_coefficients_scaled( DATASET[ 'channel' ],\n",
    "                                                                 DATASET['snrs_db'],\n",
    "                                                                 channel_estimation_noise = noise )\n",
    "    \n",
    "    channel_coeff_non_delayed=utils.calculate_channel_coefficients_scaled( DATASET[ 'channel' ],\n",
    "                                                                 DATASET['snrs_db'],\n",
    "                                                                 channel_estimation_noise = noise )\n",
    "\n",
    "\n",
    "    realized_ack = np.copy( DATASET[ 'block_success' ] )\n",
    "    \n",
    "    # Shift the channel and ACKs depending on the DELAY\n",
    "    if DELAY > 0:\n",
    "\n",
    "        channel_coeff      = channel_coeff[ : -DELAY, ... ]\n",
    "        realized_ack       = realized_ack[ DELAY: , ... ]\n",
    "        \n",
    "    test_dataset = {}\n",
    "\n",
    "    test_dataset[ 'realized_ack']   = realized_ack\n",
    "    test_dataset[ 'channel_coeff' ] = channel_coeff\n",
    "    \n",
    "#### DELAY-BLIND approach ########################################################################################\n",
    "    \n",
    "    model = load_model( ANN_MODEL_FILE_DELAYED )\n",
    "    \n",
    "    BLOCK_SIZES = DATASET[ 'block_sizes' ]\n",
    "    \n",
    "\n",
    "    \n",
    "    ann_predicted_ack_prob = predict_ann_ack_probability( test_dataset[ 'channel_coeff' ], \n",
    "                                                          NROF_MCS, \n",
    "                                                          NROF_SNRS,\n",
    "                                                          model ) \n",
    "    \n",
    "\n",
    "    ann_mcs_delayed = utils.determine_best_mcs( ann_predicted_ack_prob, BLOCK_SIZES )\n",
    "    \n",
    "\n",
    "    ann_tput_delayed = utils.calculate_average_throughput( ann_mcs_delayed, \n",
    "                                                           test_dataset[ 'realized_ack' ], \n",
    "                                                           BLOCK_SIZES)\n",
    "    \n",
    "    ann_error_rate_delayed = utils.calculate_error_rate( ann_mcs_delayed, \n",
    "                                                         test_dataset[ 'realized_ack' ] )\n",
    "#### HYBRID approach #################################################################################################\n",
    "    \n",
    "    model = load_model( ANN_MODEL_FILE )\n",
    "    \n",
    "    \n",
    "    freq_domain_channel = np.copy( test_dataset['channel_coeff'] )\n",
    "\n",
    "\n",
    "    filtered_channel_freq_response = np.ndarray( freq_domain_channel.shape, dtype = np.complex128 )\n",
    "   \n",
    "\n",
    "    for snr_index in range( NROF_SNRS ):    \n",
    "    \n",
    "        snr = 10 ** ( 0.1 * DATASET[ 'snrs_db' ][ snr_index ] )\n",
    "        \n",
    "        autocorrelation_of_reference=utils.autocorrelation( np.arange(0,N,1),\n",
    "                                                      doppler_frequency,\n",
    "                                                      channel_sampling_interval )\n",
    "\n",
    "        crosscorrelation=utils.autocorrelation( np.arange(delta,N+delta,1),\n",
    "                                          doppler_frequency,\n",
    "                                          channel_sampling_interval )\n",
    "\n",
    "        Wiener_coeff=utils.Wiener_filter_coeff_scaled( autocorrelation_of_reference,\n",
    "                                                 crosscorrelation,\n",
    "                                                  delta,\n",
    "                                                  N,\n",
    "                                                  snr,noise,\n",
    "                                                  doppler_frequency,\n",
    "                                                  channel_sampling_interval )\n",
    "        \n",
    "        for subc_index in range( NROF_SUBCARRIERS ):\n",
    "            \n",
    "            subcarrier_response = freq_domain_channel[:, subc_index, snr_index]\n",
    "        \n",
    "            #Apply the Wiener filter\n",
    "            filtered_subc_response = np.convolve( Wiener_coeff, subcarrier_response, \"full\")\n",
    "            filtered_channel_freq_response[ :, subc_index, snr_index ] = filtered_subc_response[ : -N + 1 ]\n",
    "\n",
    "    \n",
    "    ann_predicted_ack_prob_ch_pr = predict_ann_ack_probability( filtered_channel_freq_response,NROF_MCS, NROF_SNRS,model ) \n",
    "\n",
    "   \n",
    "    \n",
    "    ann_mcs_ch_pr = utils.determine_best_mcs( ann_predicted_ack_prob_ch_pr, BLOCK_SIZES )\n",
    "    \n",
    "\n",
    "    \n",
    "    ann_tput_ch_pr = utils.calculate_average_throughput( ann_mcs_ch_pr, \n",
    "                                                         test_dataset[ 'realized_ack' ], \n",
    "                                                         BLOCK_SIZES )\n",
    "\n",
    "    ann_error_rate_ch_pr = utils.calculate_error_rate( ann_mcs_ch_pr, \n",
    "                                                       test_dataset[ 'realized_ack' ] )\n",
    "#### E2E approach ######################################################################################################\n",
    "\n",
    "    end_to_end_prediction_model = load_model( E2E_MCS_PRED_MODEL )\n",
    "    \n",
    "    ann_e2e_pred_ack_prob = predict_ann_e2e_ack_probability( end_to_end_prediction_model, \n",
    "                                                             test_dataset[ 'channel_coeff' ], \n",
    "                                                             NROF_MCS,\n",
    "                                                             mem = 10 ) \n",
    "    \n",
    "\n",
    "    ann_e2e_pred_mcs = utils.determine_best_mcs( ann_e2e_pred_ack_prob, BLOCK_SIZES ) # End-to-end learning\n",
    "    \n",
    " \n",
    "\n",
    "    ann_e2e_realized_tput = utils.calculate_average_throughput( ann_e2e_pred_mcs, \n",
    "                                                                test_dataset[ 'realized_ack' ], \n",
    "                                                                BLOCK_SIZES )\n",
    "\n",
    "    ann_e2e_error_rate = utils.calculate_error_rate( ann_e2e_pred_mcs, \n",
    "                                                     test_dataset[ 'realized_ack' ] )\n",
    "    \n",
    "\n",
    " ######################################################################################################################   \n",
    "    # Compute the MSE in case of prediction and no prediction\n",
    "    mse_with_prediction = []\n",
    "    mse_no_prediction   = []\n",
    "    for snr_index in range( NROF_SNRS ):\n",
    "        snr = 10 ** ( 0.1 * DATASET[ 'snrs_db' ][ snr_index ] )\n",
    "        \n",
    "\n",
    "        noiseless_channel = channel_coeff_noiseless[ DELAY:, :, snr_index ]\n",
    "        \n",
    "   \n",
    "        delayed_channel   = test_dataset['channel_coeff'][ :, :, snr_index]\n",
    "    \n",
    "        pred_channel      = filtered_channel_freq_response[ :, :, snr_index]\n",
    "        \n",
    "        \n",
    "        mse_no_prediction.append( np.mean( np.abs( noiseless_channel - delayed_channel ) ** 2 ) / snr )\n",
    "        mse_with_prediction.append( np.mean( np.abs( noiseless_channel - pred_channel ) ** 2 ) / snr )\n",
    "        \n",
    "        \n",
    "######################################################################################################################\n",
    "    # Clear the tensorflow session and save the results\n",
    "    clear_session()\n",
    "    \n",
    "    tputs = { 'ann_delayed': ann_tput_delayed,\n",
    "              'ann_ch_pr'  : ann_tput_ch_pr,\n",
    "              'ann_e2e'    : ann_e2e_realized_tput,\n",
    "}\n",
    "        \n",
    "    error_rates = { 'ann_delayed': ann_error_rate_delayed,\n",
    "                    'ann_ch_pr'  : ann_error_rate_ch_pr,\n",
    "                    'ann_e2e'    : ann_e2e_error_rate,\n",
    " }\n",
    "    \n",
    "    mse = { 'ann_delayed': mse_no_prediction,\n",
    "            'ann_ch_pr'  : mse_with_prediction }\n",
    "    \n",
    "    ack_probabilities = { 'ann_delayed': ann_predicted_ack_prob,\n",
    "                          'ann_ch_pr'  : ann_predicted_ack_prob_ch_pr,\n",
    "                          'ann_e2e'    : ann_e2e_pred_ack_prob }\n",
    "        \n",
    "    \n",
    "    return  ( tputs,\n",
    "              error_rates,\n",
    "              mse,\n",
    "              ack_probabilities )\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
